package com.cskaoyan.javase.oop3._2inner._5lambda;

/**
 * @description: lambda表达式的基本使用
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * > Lambda 表达式是 JDK8 的一个新特性，可以取代接口的匿名内部类，写出更优雅的 Java 代码
 * > 匿名内部类实际上是局部内部类的更进一步，简化了局部内部类
 * > 那么lambda就是匿名内部类更进一步，语法上更简洁了，代码更优雅了，是高端的玩法,是人上人的玩法
 *
 * 通过以上描述总结:
 *      1.lambda表达式仍然是局部内部类,仍然定义在局部位置(今天以方法为例)
 *      2.lambda表达式不能完全取代匿名内部类,只能取代接口的匿名内部类(lambda表达式只能创建接口的子类对象)
 *      3.lambda表达式的本质也是对象,并且是接口的子类对象
 *
 * lambda表达式对接口是有自己独特的要求的,不是任何接口都能使用lambda表达式创建子类对象
 * lambda表达式要求接口必须只有一个需要实现的抽象方法,也必须有一个(不能没有抽象方法)
 *      这种接口在Java当中称之为"功能接口",功能接口使用注解@FunctionalInterface标记它
 *      只有是功能接口的接口才不会报错
 *      注意事项:
 *          1.功能接口中只能有一个方法吗?
 *              功能接口只要求接口中必须只有一个需要实现的抽象方法,但是对于不需要实现的方法是没有要求
 *              比如Java8之后的默认方法,静态方法,增加这些方法是不会报错的
 *          2.功能接口中只能有一个抽象方法吗?
 *              不是,什么样的抽象方法不需要被子类实现呢?
 *              Java当中任何类都是Object类的子类,
 *              如果一个抽象方法是Object当中已经存在的成员方法,那么这个抽象方法就不需要被子类实现
 *          3.当然绝大多数情况,功能接口还是只有一个方法,就是那个需要被实现的抽象方法
 *
 * 功能接口有了,就可以使用lambda表达式创建它的子类对象了
 * 语法:
 *      (形参列表) -> {
 *          //方法体
 *      }
 *
 *      逐一解释:
 *         形参列表是功能接口中那个要实现的抽象方法的形参列表,直接抄下来即可
 *         -> 是lambda表达式运算符,读叫"goes to"
 *         {
 *             //方法体
 *         }
 *         是功能接口中那个要实现的抽象方法的重写的方法体,这里自由发挥
 *
 * 为什么lambda表达式要求接口必须是功能接口?
 *      语法限制了它,它只能重写一个方法,而且也不能新增属于子类自己的成员
 *
 * 按照语法写完lambda表达式,加上分号后,直接报错,为什么?
 * 直接写语法,会导致编译器无法得知lambda创建子类对象的类型而Java是强类型语言,任何变量都要有它的类型
 * 所以直接写语法就会报错
 * 我们缺少一个重要的步骤:
 *      告诉编译器lambda表达式创建的子类对象,到底是哪个功能接口的子类对象
 * 这个步骤称之为"lambda表达式的类型推断"
 * 我们必须要通过一定手段,帮助编译器推断出lambda表达式的类型
 * 怎么推断?
 * 需要额外信息
 * 需要写额外的代码,让编译器完成类型推断,主要有以下方式:
 *      1,直接接收,用功能接口去接收它,就是用父类接收
 *      2,不接收,但是仍然告诉编译器类型,语法:
 *         ((功能接口的名字)lambda表达式).抽象方法
 *      3,借助方法完成类型推断
 *          a.借助形参
 *          b.借助返回值类型
 *
 * 以上,lambda表达式的基本使用就完成了,但是这时的lambda表达式仍然比较复杂,还有简化的余地
 *
 */
public class Demo {
    public static void main(String[] args) {
        //lambda仍然是局部内部类,写在方法内部
        //Not a statement
        IA ia = (int a) -> {
            System.out.println("我是lambda表达式,我是仌" + a);
        };
        ia.test(100);

        ((IB) (int a) -> {
            System.out.println("hello world!" + a);
        }).test(200);

        //编译器知道要传入IA的子类对象,依据这一点,可以直接写lambda表达式
        method((int a) -> {
            System.out.println("依据方法的形参完成推断" + a);
        });

        method().test(300);

    }

    public static void method(IA a) {
        a.test(100);
    }

    public static IB method(){
        //编译器知道要返回IB的子类对象,依据这一点,可以直接写lambda表达式
        return (int a) -> {
            System.out.println("依据方法的返回值完成推断" + a);
        };
    }
}


@FunctionalInterface
interface IA {
    void test(int a);
}

@FunctionalInterface
interface IB {
    void test(int a);
}
/*
@FunctionalInterface
interface ITest{
    void test();
    //void test2();

    //把Object类当中的equals方法写成抽象方法,它不需要被子类实现,不影响功能接口
    boolean equals(Object obj);

    default void test2(){}
    static void test3(){}
}
class A implements ITest{
    @Override
    public void test() {

    }
}*/
